/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014年7月16日
 * V4.0
 */
package com.jphenix.kernel.script;

import com.jphenix.clazz.ClassFile;
import com.jphenix.driver.nodehandler.FNodeHandler;
import com.jphenix.share.lang.SBoolean;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.lang.SInteger;
import com.jphenix.share.lang.SString;
import com.jphenix.share.tools.MD5;
import com.jphenix.share.util.*;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.docs.ScriptInfo;
import com.jphenix.standard.script.IScriptLoader;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

/**
 * 脚本文件信息类
 * 
 * 注意：（190121）
 *        不能使每个脚本使用独立的类加载器（当初这么设计是想做单独脚本挂载专用包，即某个脚本加载指定的扩展包路径下所有的jar包）
 *        因为在脚本加载器构建脚本时，用的是脚本加载器自己的类加载器。如果某个脚本中引用了扩展包路径（即：这个脚本自己的类加载器
 *        中引用了扩展包路径中所有的jar包），但是脚本加载器所使用的类加载器并没有这些jar包的引用路径，无法构建这个脚本。
 *        
 *        所以脚本加载器中需要引用所有脚本类。
 *        
 *        那某个脚本引用了扩展包路径怎么办？ 只能通过这个脚本的影子类（即：脚本加载器构建这个脚本时，实际上是构建了这个脚本的影子类
 *        ，而在影子类中，构建一个新的子类加载器，引用扩展包路径中的所有jar包，和引用这个脚本类。然后通过类反射方式调用这个脚本）
 *        
 *        所以，无法实现传统类挂载专用包（即：只有某个传统类引用了扩展类路径中的全部jar包。并不是真的无法实现，只是非常麻烦，需要自
 *        动生成这个传统类的影子类，并实现这个影子类中，全部的方法，通过反射方式调用实际的传统类）
 *        
 *        扩展包路径是解决什么问题的呢？
 *        
 *        如果实现了个别类（脚本）加载扩展包中的全部jar包，而其他类（脚本）不用加载，可以实现指定类加载指定版本的功能，比如PIO操作
 *        Excel库的功能。别的类（脚本）可以加载另外一个版本的PIO库（一组jar包）
 *        
 *        并且解决了，两组功能库中的相同jar包版本冲突的问题。即：假如PIO中有 common-log.jar ，Exchange中也有common-log.jar，但是这两
 *        个同样的common-log.jar是两个版本（一个新版本，一个旧版本），而且PIO和Exchange只能使用各自的版本。按照传统的做法，需要将这
 *        两组库中所有的jar包文件全部放到WEB-INF/lib文件夹中，并且通过设置class_path优先使用哪个common-log.jar。时间一长，各种相同包
 *        不同版本都要使用的情况会非常多，导致WEB-INF/lib中含有大量的jar包，而且很多都是重复的。
 * 
 * 
 * 
 * 2018-08-06 增加了纯java类脚本托管父类功能，即框架调用父类中定义的_executeX方法，可由父类调用其子类脚本中执行入口_execute方法
 * 2018-11-01 如果是导入的脚本，并且导入后没有修改过里面的内容，则版本号使用导入脚本的版本号
 * 2018-11-19 增加了创建时间信息
 * 2018-12-05 修改了脚本注释为空时，错误的获取到第一个传入参数的注释作为脚本注释
 * 2019-01-11 注册主服务脚本（实现了接口IBeanRegister的脚本）如果在热部署后，就失去了之前注册进来的脚本信息。已经解决这问题
 * 2019-01-16 每个脚本用自己的类加载器，支持个别脚本加载扩展库
 * 2019-01-21 去掉了脚本信息类中的类加载器（原因请看 注意（190121）)
 *            去掉了isStatic属性，从来没这么用过
 * 2019-01-28 增加了保存上次执行错误信息
 * 2019-06-13 增加了默认值，是否需要做Base64解码参数
 * 2019-07-20 将信息中的绝对路径改为相对路径
 * 2019-10-30 增加了上次修改内容说明信息
 * 2019-11-01 修改了错误标记，既可以存在历史错误信息，又可以清除错误标记
 *            增加了运行错误标记，既可以存在历史运行时错误信息，又可以清除运行时错误标记
 * 2020-04-15 完善了clone方法中遗漏的变量
 * 2020-09-09 增加了传入参数变量是否为类变量属性
 * 2020-09-21 格式化了局部代码
 * 2021-03-17 格式化了一下代码
 * 
 * @author 马宝刚
 * 2014年7月16日
 */
@ClassInfo({"2021-03-17 21:08","脚本文件信息类"})
public class ScriptVO {

  public final static String SCRIPT_CLASS_HEAD = "___"; //生成的脚本类的头字母
  
  public ScriptLoader sl               = null;   //脚本加载器
  
  //不能这么使用，请看 注意（190121）
  //public ScriptClassLoader scl       = null;   //脚本类加载器
  
  //不能这么使用 需要将脚本类对象缓存到 ScriptClassLoader中，因为脚本类中如果引用了别的脚本类
  //需要ScriptClassLoader直接从缓存中拿到那个脚本类，如果缓存在ScriptVO中，就只能拿到自己的类对象
  //public Class<?> scriptCls          = null;   //脚本类对象
  
  //取消静态脚本，从来就没这么用过
  //public boolean isStatic            = false;  //是否为静态脚本
  
  public String id                     = null;   //脚本主键
  public String className              = null;   //类名（不包含包路径）注意：类名不可以为脚本主键，因为脚本主键可以是纯数字，类名不可以
  public String initMethodName         = null;   //初始化方法
  public String afterStartMethodName   = null;   //程序都启动好以后，需要调用的方法
  public String destoryMethodName      = null;   //终止程序前调用的方法
  public String registerID             = null;   //注册类主键（脚本主键或类主键）
  
  //如果当前类是注册主服务，该序列中保存的是注册到当前服务的类主键序列
  //如果当前脚本进行了热部署，就需要通过该序列，将之前已经注册进来的脚本
  //在当前热部署后，重新执行注册，否则注册主服务热部署（重新初始化）候，
  //就无法使用之前注册进来的脚本了。
  //只有脚本存在这种情况，jar包中的传统类不存在热部署，所以不存在这种情况
  public List<String> registerBeanIDs  = null;
  
  public boolean  isShadow             = false;  //该脚本类是否为影子类
  public boolean  disabled             = false;  //是否禁止使用
  public int      singletonLevel       = 0;      //驻留内存等级
  public String   sourceFilePath       = null;   //源文件路径
  public String   buildSourceFilePath  = null;   //编译源文件（java文件）路径
  public String   classFilePath        = null;   //编译后的文件路径
  public Object   scriptObj            = null;   //脚本类实例
  public String   sourceFileVer        = null;   //源文件版本号
  public String   classFileVer         = null;   //编译后的文件版本号
  public long     sourceFileTime       = 0;      //源文件最后更新时间
  public long     classFileTime        = 0;      //编译后的文件最后更新时间
  public String   subPath              = null;   //脚本文件相对路径 （不包含文件名）
  public String   classPath            = null;   //类路径
  public String   title                = null;   //脚本标题
  public String   explain              = null;   //脚本说明
  public String   sourceContent        = null;   //代码内容（被编译为Base64格式，因为内容中也会出现xml内容，与脚本保存格式冲突）
  public String   errorMsg             = null;   //编译时的错误信息
  public boolean  errorFlag            = false;  //是否存在错误
  public int      sourceFileStatus     = 0;      //没变化  1新增  2更新
  public boolean  actionSpecialCode    = false;  //是否允许提交脚本代码
  public boolean  noOutLog             = false;  //是否禁止输出日志（当前线程）
  public boolean  isParent             = false;  //是否为父类脚本
  public String   parentBeanID         = null;   //父类主键
  public String   interfaceClasspath   = null;   //引用其他接口类路径（多个用逗号分隔）
  public String[] extLibPaths          = null;   //该脚本如果使用了扩展类，指定类所在的子文件夹路径。 相对于 /WEB-INF/ext_lib 根路径
  public boolean  haveExtLibPaths      = false;  //是否存在扩展包路径

  public boolean  hasSourceFile        = false;  //是否存在源文件
  public boolean  isDbTransaction      = false;  //是否启用数据库事务处理
  public boolean  isClassVar           = false;  //声明的变量是否都是类变量
  
  public long     taskRunCount         = 0;      //如果是任务脚本，已经执行了多少次
  public long     maxExecuteTime       = 0;      //最长执行时间
  public long     minExecuteTime       = 0;      //最短执行时间
  public long     lastExecuteTime      = 0;      //上一次执行时间
  
  //是否只有主服务器才运行该任务服务，其它服务照样初始化
  public boolean clusterMasterTaskRun  = false;
  //当前动作脚本是否仅集群成员调用（只针对通过http协议直接访问的动作类）
  public boolean clusterCallOnly       =     false;
  
  //取消了这个变量值，原本是调用这个脚本时，由这个脚本的这个属性判断是否调用主服务器的
  //这个脚本，但这样做遇到个问题，这个脚本通过传入不同的参数判断可能调用本地功能，可能
  //调用主服务器功能，这就无法实现了。故改为在调用时通过传入控制参数实现是否调用主服务器
  //脚本
  
  //为true时： 在集群模式中，如果不是主服务器，则不会初始化并常驻内存该脚本
  //当本地其它脚本调用该脚本时，会直接调用远程主服务器中的这个脚本，并获取返回值
  //public boolean clusterCallMaster = false; //在集群模式中，是否调用主服务器功能
  
  //同上
  //为true时，在集群模式中，其他脚本调用这个脚本时，实际上调用了所有集群服务器中的这个脚本
  //public boolean clusterReceive    = false;
  
  /*
   * 之前遇到过数据重复提交的问题，在一个动作类中，先查询了一下数据库判断数据是否存在
   * 如果不存在，再新增记录。但是从数据上来看，出现了重复数据（判断没起作用）
   * 经过分析发现这两条记录提交间隔时间只相差毫秒级别，甚至同一毫秒，导致这个用户两次
   * 发起的动作几乎同一时间判断的数据是否存在，最后得到的结果都是不存在，于是两次同时
   * 发起的动作同时执行了插入数据，导致数据重复。
   * 
   * 增加这个变量避免同一个用户，在当前动作没执行完时，避免再次执行。仅限于同一个用户
   * 这样不会影响性能
   * 
   * 默认是禁止重复提交（当前这个用户的这个动作还没执行完，不能再次执行），也可以设置
   * 打开允许重复提交（目前想不通什么情况下要求打开，通常都是禁止的）
   */
  //是否允许动作类同一个用户并行执行
  public boolean enabledActionCharge = false;
  
  /*
   * 独立的类：
   * 类名需要人工来起，包路径都是统一的。
   * 这样就实现了传统的java类也可以用脚本来写，这样就非常方便灵活了。
   * 注意：独立的类并不需要<%!  !%>做标识，也不支持脚本语法。除了不
   * 用写包路径，其它的都跟传统的类一样
   */
  public boolean independentClass = false;  //是否为独立的类
  
  /*
   * 独立的类中包含了类版本号，采用这个版本号，所以在每次编译后，要读取出该版本号
   * 回写到源码脚本中
   * 
   * 如果是在启动时或者自动检测时自动编译脚本，则在编译后修改源码中的版本号然后回写源码脚本
   * 如果手工新增或者修改，就不在编译后自动回写，而是在手动保存方法最后统一写入源码脚本
   * 
   * 只有手工新增或修改时，该值为true
   * 
   */
  public boolean independentClassAfterSave = false;
  
    //脚本方法带 synchronized 关键字。
  //必须是常驻内存的脚本
  public boolean isSync                    = false; //是否为同步脚本
  
  //是否为同步脚本动作提交
  //同步脚本提交时，版本号是不用刷新的
  public boolean isSyncSubmit              = false;
  
  //引用的其它脚本主键序列  主要用来判断因为其它脚本发生变化，是否需要重新编译当前脚本
  public List<String> importScriptIdList      = new ArrayList<String>();
  
  //引用的其它需要路由执行的脚本主键序列 主要用来统计需要代理的脚本信息
  public List<String> importRouteScriptIdList = new ArrayList<String>();
  
  //取消传入参数是序列，从来没这么用过,而且也有办法不用序列
  //public boolean inFieldList          = false;                          //传入参数是否为序列
  
  public List<ScriptFieldVO> inFields = new ArrayList<ScriptFieldVO>(); //传入参数信息容器
  public String outType               = null;                           //返回值类型
  public String outExplain            = null;                           //返回值说明
  
  //是否单个脚本暂不编译
  //只针对当前单个脚本，对批量编译无效，对启动时编译无效
  //该变量不保存到脚本文件中
  public boolean noSingleCompile = false;
  
  public String loaderVer        = null;    //编译时，脚本加载器的版本号
  
  //如果是内置在框架包中的源码脚本，标记为内置脚本 
  //内置脚本可以修改，修改后就会在外部生成新的脚本
  //最终使用这个脚本
  //这个值不是由页面提交上来做修改的。而是通过程序判断后得来的
  public boolean nativeScript    = false;  //是否为内置脚本
  
  //如果是内置脚本，标记为true
  //即使该脚本被覆盖，这个值仍旧为true
  //这个值不是由页面提交上来做修改的。而是通过程序判断后得来的
  public boolean   hasNativeScript = false;  //是否存在被覆盖的内置脚本
  
  //注意：该值与nativeScript不同，nativeScript是存在内部源码，该值是存在编译后的内部类
  public boolean   nativeClass     = false;  //是否为内置的编译脚本类
  public String    nativeClassPath = null;   //压缩文件中的编译文件路径
  
  public boolean   hasNativeClass  = false;  //存在内置的编译脚本类
  
  public String    devCode         = null; //开发商代码
  public String    devName         = null; //开发商名称
  public String    uUser           = null; //更新人用户主键
  public String    uMan            = null; //更新人姓名
  public String    uCount          = null; //更新次数
  public String    modifyContent   = null; //上次修改内容
  
  public String    createTime      = null; //脚本创建时间
  
  //是否为动作类
  //这个变量值，在初始化脚本类时，判断父类是否实现了IActionBean接口
  //没必要用注解方式标记到脚本类中，直接判断父类就可以了
  public boolean   isActionBean    = false;
  
  public ClassFile cf              = null;  //解读类字节数组处理类
  public boolean   hasNewRunError  = false;
  public long      runErrorCount   = 0;     //发生错误数量
  public String    runErrorMsg     = null;  //最后一次发生错误信息
  public String    runErrorTime    = null;  //最后一次发生错误时间
  
  /**
   * 构造函数
   * @author 马宝刚
   */
  public ScriptVO() {
    super();
  }
  
  /**
   * 构造函数
   * @author 马宝刚
   */
  public ScriptVO(IViewHandler vh) {
    super();
    setValueFromVh(vh);
  }
  
  /**
   * 构造函数
   * @author 马宝刚
   */
  public ScriptVO(ScriptLoader sl) {
    super();
    this.sl = sl;
  }
  
  /**
   * 构造函数
   * @author 马宝刚
   */
  public ScriptVO(ScriptLoader sl,IViewHandler vh) {
    super();
    this.sl = sl;
    setValueFromVh(vh);
  }
  
  /**
   * 是否存在错误
   * @return 是否存在错误
   * 2014年8月15日
   * @author 马宝刚
   */
  public boolean hasError() {
    return errorFlag;
  }
  
  
  /**
   * 添加错误信息
   * @param msg 错误信息
   * 2014年8月15日
   * @author 马宝刚
   */
  public void addErrorMsg(Object msg) {
    if(msg==null) {
      return;
    }
    errorFlag = true;
    if(errorMsg!=null && errorMsg.length()>0 && errorMsg.length()<102400) {
      errorMsg += "\n\n\n"+msg;
    }else {
      errorMsg = msg.toString();
    }
  }
  
  /**
   * 清空错误信息
   * 2019年11月1日
   * @author MBG
   */
  public void resetError() {
    errorFlag = false;
    errorMsg  = null;
  }
  
  /**
   * 克隆
   */
  @Override
    public ScriptVO clone() {
    //构建返回值
    ScriptVO res = new ScriptVO();
    res.sl = sl;
    
    res.independentClass          = independentClass;
    
    res.afterStartMethodName      = afterStartMethodName;
    res.buildSourceFilePath       = buildSourceFilePath;
    res.classFileTime             = classFileTime;
    res.classFilePath             = classFilePath;
    //res.isStatic                = isStatic;
    res.classFileVer              = classFileVer;
    res.classPath                 = classPath;
    res.destoryMethodName         = destoryMethodName;
    res.disabled                  = disabled;
    res.errorMsg                  = errorMsg;
    res.errorFlag                 = errorFlag;
    res.actionSpecialCode         = actionSpecialCode;
    res.noOutLog                  = noOutLog;
    res.isSync                    = isSync;
    res.explain                   = explain;
    res.id                        = id;
    res.className                 = className;
    res.importScriptIdList        = new ArrayList<String>();
    for(String scriptId:importScriptIdList) {
      res.importScriptIdList.add(scriptId);
    }
    res.importRouteScriptIdList   = new ArrayList<String>();
    for(String scriptId:importRouteScriptIdList) {
      res.importRouteScriptIdList.add(scriptId);
    }
    //res.inFieldList             = inFieldList;
    
    res.inFields                  = new ArrayList<ScriptFieldVO>();
    for(ScriptFieldVO sfVO:inFields) {
      res.inFields.add(sfVO.clone());
    }
    res.initMethodName            = initMethodName;
    res.isShadow                  = isShadow;
    res.isDbTransaction           = isDbTransaction;
    res.isClassVar                = isClassVar;
    res.outExplain                = outExplain;
    res.outType                   = outType;
    res.parentBeanID              = parentBeanID;
    res.registerID                = registerID;
    if(registerBeanIDs!=null) {
      res.registerBeanIDs = new ArrayList<String>();
      for(String scriptId:registerBeanIDs) {
        res.registerBeanIDs.add(scriptId);
      }
    }
    res.scriptObj                 = scriptObj;
    res.singletonLevel            = singletonLevel;
    res.sourceContent             = sourceContent;
    res.sourceFilePath            = sourceFilePath;
    res.sourceFileTime            = sourceFileTime;
    res.sourceFileVer             = sourceFileVer;
    res.subPath                   = subPath;
    res.title                     = title;
    res.isParent                  = isParent;
    res.interfaceClasspath        = interfaceClasspath;
    res.extLibPaths               = extLibPaths;
    res.haveExtLibPaths           = haveExtLibPaths; 
    res.isActionBean              = isActionBean;
                                  
    res.taskRunCount              = taskRunCount;
    res.maxExecuteTime            = maxExecuteTime;
    res.minExecuteTime            = minExecuteTime;
    res.lastExecuteTime           = lastExecuteTime;
    res.runErrorCount             = runErrorCount;
    res.runErrorMsg               = runErrorMsg;
    res.hasNewRunError            = hasNewRunError;
    res.runErrorTime              = runErrorTime;
    
    //已取消，请看变量声明处的注释
    //res.clusterCallMaster       = clusterCallMaster;
    //res.clusterReceive          = clusterReceive;
    res.clusterMasterTaskRun      = clusterMasterTaskRun;
    res.clusterCallOnly           = clusterCallOnly;
    res.enabledActionCharge       = enabledActionCharge;
    
    res.nativeScript              = nativeScript;
    res.hasNativeScript           = hasNativeScript;
    res.nativeClass               = nativeClass;
    res.hasNativeClass            = hasNativeClass;
    res.nativeClassPath           = nativeClassPath;
    res.createTime                = createTime;
                                  
    res.sourceFileStatus          = sourceFileStatus;
    res.hasSourceFile             = hasSourceFile;
    res.independentClassAfterSave = independentClassAfterSave;
    res.isSyncSubmit              = isSyncSubmit;
    res.noSingleCompile           = noSingleCompile;
      res.loaderVer                 = loaderVer;
    
      res.devCode                   = devCode;
      res.devName                   = devName;
      res.uUser                     = uUser;
      res.uMan                      = uMan;
      res.uCount                    = uCount;
      res.modifyContent             = modifyContent;
      res.cf                        = cf;
    return res;
  }
  
  /**
   * 返回xml信息
   * @param forSaveFile 是否将返回值保存到文件中
   * @return xml信息
   * 2014年8月16日
   * @author 马宝刚
   */
  protected String toXml(boolean forSaveFile) {
      String fileSn = ""; //文件时间戳
      if(sourceFilePath!=null) {
          fileSn = String.valueOf((new File(sl.getSourceBasePath()+sourceFilePath)).lastModified());
      }
    //构建返回值
    StringBuffer reSbf = new StringBuffer();
    
    String value; //值
    
    reSbf
      .append("<?xml version=\"1.0\" encoding='UTF-8'?>\n")
      .append("<root>\n");
    if(nativeScript) {
      //如果当前脚本是内置脚本，标记为1。内置脚本也可以被覆盖
      //覆盖后的脚本保存在外部文件夹中
      reSbf.append("\t<native_script>1</native_script>\n");
    }
    if(hasNativeScript) {
      //如果这个脚本存在对应的内置脚本，则标记为1.无论内置脚本是否被覆盖
      //这个值一直为1
      reSbf.append("\t<has_native_script>1</has_native_script>\n");
    }
    if(nativeClass) {
      //是否为内置编译类
      reSbf.append("\t<native_class>1</native_class>\n");
      reSbf.append("\t<native_class_path>")
      .append(SString.valueOf(nativeClassPath)).append("</native_class_path>\n");
    }
    if(hasNativeClass) {
      //是否存在内置编译类
      reSbf.append("\t<has_native_class>1</has_native_class>\n");
    }
    value = SString.valueOf(errorMsg);
    if(value.length()>0) {
      reSbf.append("\t<error_msg><![CDATA[").append(value).append("]]></error_msg>\n");
    }
    if(!forSaveFile) {
      reSbf.append("\t<id>").append(SString.valueOf(id)).append("</id>\n")
      .append("\t<file_sn>").append(fileSn).append("</file_sn>\n") //加入当前源文件的时间戳
      .append("\t<sub_path>").append(SString.valueOf(subPath)).append("</sub_path>\n");
    }
    
    if(independentClass) {
        reSbf.append("\t<independent_class>1</independent_class>\n");
    }
        
    reSbf
      //类路径不要保存到文件中，也没必要输出
      //.append("\t<class_name>").append(SString.valueOf(className)).append("</class_name>\n")
      .append("\t<title>").append(SString.valueOf(title)).append("</title>\n")
      .append("\t<create_time>").append(SString.valueOf(createTime)).append("</create_time>\n")
      .append("\t<ver>").append(SString.valueOf(sourceFileVer)).append("</ver>\n")
      .append("\t<class_ver>").append(SString.valueOf(classFileVer)).append("</class_ver>\n");
    
    value = SString.valueOf(explain);
    if(value.length()>0) {
      reSbf
        .append("\t<explain><![CDATA[").append(value).append("]]></explain>\n");
    }
    value = SString.valueOf(afterStartMethodName);
    if(value.length()>0) {
      reSbf.append("\t<after_start_method>").append(value).append("</after_start_method>\n");
    }
    value = SString.valueOf(destoryMethodName);
    if(value.length()>0) {
      reSbf.append("\t<destory_method>").append(value).append("</destory_method>\n");
    }
    value = SString.valueOf(initMethodName);
    if(value.length()>0) {
      reSbf.append("\t<init_method>").append(value).append("</init_method>\n");
    }
    //if(isStatic) {
    //  reSbf.append("\t<is_static>1</is_static>\n");
    //}
    if(actionSpecialCode) {
      reSbf.append("\t<action_special_code>1</action_special_code>\n");
    }
    if(isShadow) {
      reSbf.append("\t<is_shadow>1</is_shadow>\n");
    }
    
    //已取消，请看变量声明处的注释
    //if(clusterCallMaster) {
    //  reSbf.append("\t<cluster_call_master>1</cluster_call_master>\n");
    //}
    //if(clusterReceive) {
    //  reSbf.append("\t<cluster_receive>1</cluster_receive>\n");
    //}
    
    if(enabledActionCharge) {
      reSbf.append("\t<enabled_action_charge>1</enabled_action_charge>\n");
    }
    if(clusterMasterTaskRun) {
      reSbf.append("\t<cluster_master_task_run>1</cluster_master_task_run>\n");
    }
    if(clusterCallOnly) {
      reSbf.append("\t<cluster_call_only>1</cluster_call_only>\n");
    }
    if(isDbTransaction) {
      reSbf.append("\t<is_db_transaction>1</is_db_transaction>\n");
    }
    if(isClassVar) {
      reSbf.append("\t<is_class_var>1</is_class_var>\n");
    }
    if(noOutLog) {
      reSbf.append("\t<no_out_log>1</no_out_log>\n");
    }
        if(isParent) {
            reSbf.append("\t<is_parent>1</is_parent>\n");
        }
        if(isSync) {
            reSbf.append("\t<is_sync>1</is_sync>\n");
        }
    value = SString.valueOf(registerID);
    if(value.length()>0) {
      reSbf.append("\t<register_id>").append(value).append("</register_id>\n");
    }
    if(disabled) {
      reSbf.append("\t<disabled>1</disabled>\n");
    }
    if(singletonLevel!=0) {
      reSbf.append("\t<singleton_level>").append(singletonLevel).append("</singleton_level>\n");
    }
    value = SString.valueOf(parentBeanID);
    if(value.length()>0) {
      reSbf.append("\t<parent_bean_id>").append(value).append("</parent_bean_id>\n");
    }
    if(interfaceClasspath!=null && interfaceClasspath.length()>0) {
      reSbf.append("\t<interface_classpath>").append(interfaceClasspath).append("</interface_classpath>\n");
    }
    if(extLibPaths!=null && extLibPaths.length>0) {
      reSbf.append("\t<ext_lib_paths>").append(StringUtil.arr2str(extLibPaths,";")).append("</ext_lib_paths>\n");
    }
    
    if(importScriptIdList.size()>0) {
      reSbf.append("\t<load_script>\n");
      String title; //脚本标题
      for(String sId:importScriptIdList) {
        value = SString.valueOf(sId);
        if(sl!=null) {
          title = sl.getScriptTitle(value);
        }else {
          title = "";
        }
        if(value.length()>0) {
          reSbf.append("\t\t<value title=\"").append(title).append("\">").append(value).append("</value>\n");
        }
      }
      reSbf.append("\t</load_script>\n");
    }
    
    if(importRouteScriptIdList.size()>0) {
      reSbf.append("\t<route_script>\n");
      for(String sId:importRouteScriptIdList) {
        value = SString.valueOf(sId);
        if(value.length()>0) {
          reSbf.append("\t\t<value>").append(value).append("</value>\n");
        }
      }
      reSbf.append("\t</route_script>\n");
    }
    
    //处理传入参数
    if(inFields.size()>0) {
      
      //取消了传参为list方式
//      if(inFieldList) {
//        reSbf.append("\t<in list=\"true\">\n");
//      }else {
        reSbf.append("\t<in>\n");
//      }
      for(ScriptFieldVO sfVO:inFields) {
        if(sfVO.isStaticPara) {
          continue;
        }
        reSbf
          .append("\t\t<field>\n")
          .append("\t\t\t<name>").append(SString.valueOf(sfVO.name)).append("</name>\n")
          .append("\t\t\t<type>").append(SString.valueOf(sfVO.type)).append("</type>\n");
          if(sfVO.notNull) {
            reSbf.append("\t\t\t<not_null>true</not_null>\n");
          }
          if(sfVO.defValue!=null && sfVO.defValue.length()>0) {
            reSbf.append("\t\t\t<default_value>").append(sfVO.defValue).append("</default_value>\n");
          }
          if(sfVO.needB64Dec) {
            reSbf.append("\t\t\t<base64_deccode>true</base64_deccode>\n");
          }
          value = SString.valueOf(sfVO.explain);
          if(value.length()>0) {
            reSbf
              .append("\t\t\t<explain><![CDATA[").append(value).append("]]></explain>\n");
          }
          reSbf.append("\t\t</field>\n");
      }
      reSbf.append("\t</in>\n");
    }
    
    //处理返回值
    if((outType!=null && outType.length()>0) || (outExplain!=null && outExplain.length()>0)) {
      reSbf
        .append("\t<out>\n")
        .append("\t\t<type>").append(SString.valueOf(outType)).append("</type>\n");
      value = SString.valueOf(outExplain);
      if(value.length()>0) {
        reSbf
          .append("\t\t<explain><![CDATA[").append(value).append("]]></explain>\n");
      }
      reSbf.append("\t</out>\n");
    }
    //放入维护信息
    reSbf
      .append("\t<oper_dev_code>").append(SString.valueOf(devCode)).append("</oper_dev_code>\n")
      .append("\t<oper_dev_name>").append(SString.valueOf(devName)).append("</oper_dev_name>\n")
      .append("\t<oper_dev_uuser>").append(SString.valueOf(uUser)).append("</oper_dev_uuser>\n")
      .append("\t<oper_dev_uman>").append(SString.valueOf(uMan)).append("</oper_dev_uman>\n")
      .append("\t<oper_dev_ucount>").append(SString.valueOf(uCount)).append("</oper_dev_ucount>\n")
      .append("\t<modify_content><![CDATA[").append(modifyContent).append("]]></modify_content>\n");
    
    //放入性能 信息
    reSbf
      .append("\t<taskRunCount>").append(taskRunCount).append("</taskRunCount>\n")
      .append("\t<maxExecuteTime>").append(taskRunCount).append("</maxExecuteTime>\n")
      .append("\t<minExecuteTime>").append(taskRunCount).append("</minExecuteTime>\n")
      .append("\t<lastExecuteTime>").append(taskRunCount).append("</lastExecuteTime>\n")
      .append("\t<runErrorCount>").append(runErrorCount).append("</runErrorCount>\n")
      .append("\t<runErrorTime>").append(str(runErrorCount)).append("</runErrorTime>\n")
      .append("\t<runErrorMsg><![CDATA[").append(str(runErrorMsg)).append("]]></runErrorMsg>\n")
      .append("\t<hasNewRunError>").append(hasNewRunError?"1":"0").append("</hasNewRunError>\n");
    
    //放入代码内容
    reSbf
      .append("\t<content><![CDATA[").append(SString.valueOf(sourceContent)).append("]]></content>\n");
    
    reSbf.append("</root>");
    
    return reSbf.toString();
  }
  
  /**
   * 返回XML格式的信息
   * @return XML格式的信息
   * 2014年8月12日
   * @author 马宝刚
   */
  public String toXml() {
    return toXml(false);
  }
  
  /**
   * 将视图对象中的值设置到当前类中
   * @param vh
   * 2014年8月11日
   * @author 马宝刚
   */
  public void setValueFromVh(IViewHandler vh) {
    vh = vh.getFirstChildNodeByNodeName("root");
    //相对路径
    String vhValue = vh.getFirstChildNodeByNodeName("sub_path").nt().trim();
    if(vhValue.length()>0) {
      subPath = vhValue;
      if (subPath.startsWith("/")) {
        subPath = subPath.substring(1);
      }
      if (subPath.endsWith("/")) {
        subPath = subPath.substring(0, subPath.length() - 1);
      }
    }
    //设置是否为同步功能提交
    isSyncSubmit = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_sync_submit").nt().trim());
    
    //本次是否需要编译
    noSingleCompile = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("no_single_compile").nt().trim());
    
    //在提交脚本时会提交id值
    vhValue = vh.getFirstChildNodeByNodeName("id").nt().trim();
    if(vhValue.length()>0) {
      id = vhValue;
    }
    independentClass         = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("independent_class").nt().trim());
    if(independentClass) {
      className            = id;
    }else{
      className            = SCRIPT_CLASS_HEAD+id;
    }
    //通常页面提交信息中是不带版本值的，但如果是通过导入方式进来的脚本，或者是脚本同步方式更新的脚本，就会带版本号值
    sourceFileVer            = vh.getFirstChildNodeByNodeName("ver").nt().trim();
    classPath                = IScriptLoader.SCRIPT_PACKAGE+"."+className;
    title                    = vh.fonn("title").nt().trim();
    createTime               = vh.getFirstChildNodeByNodeName("create_time").nt().trim();
    if(createTime.length()<1) {
      createTime = SDate.nowDateTimeString();
    }
    explain                  = vh.fonn("explain").nt();
    parentBeanID             = vh.getFirstChildNodeByNodeName("parent_bean_id").nt().trim();
    interfaceClasspath       = vh.getFirstChildNodeByNodeName("interface_classpath").nt().trim();
    extLibPaths              = BaseUtil.split(vh.getFirstChildNodeByNodeName("ext_lib_paths").nt().trim(),";","；",":","：",",","，");
    for(int i=0;i<extLibPaths.length;i++) {
      if(extLibPaths[i].startsWith("/")) {
        //去掉开头的路径分隔符
        extLibPaths[i] = extLibPaths[i].substring(1);
      }
    }
    haveExtLibPaths = extLibPaths != null && extLibPaths.length > 0;
    initMethodName           = vh.getFirstChildNodeByNodeName("init_method").nt().trim();
    afterStartMethodName     = vh.getFirstChildNodeByNodeName("after_start_method").nt().trim();
    destoryMethodName        = vh.getFirstChildNodeByNodeName("destory_method").nt().trim();
    registerID               = vh.getFirstChildNodeByNodeName("register_id").nt().trim();
    //isStatic                 = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_static").nt().trim());
    isParent                 = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_parent").nt().trim());
    actionSpecialCode        = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("action_special_code").nt().trim());
    isShadow                 = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_shadow").nt().trim());
    //已取消，请看变量声    明处的注释
    //clusterCallMaster      = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("cluster_call_master").nt().trim());
    //clusterReceive         = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("cluster_receive").nt().trim());
    clusterMasterTaskRun     = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("cluster_master_task_run").nt().trim());
    clusterCallOnly          = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("cluster_call_only").nt().trim());

    enabledActionCharge      = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("enabled_action_charge").nt().trim());

    isDbTransaction          = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_db_transaction").nt().trim());
    isClassVar               = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_class_var").nt().trim());
    noOutLog                 = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("no_out_log").nt().trim());
    isSync                   = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("is_sync").nt().trim());
    disabled                 = SBoolean.valueOf(vh.getFirstChildNodeByNodeName("disabled").nt().trim());
    singletonLevel           = SInteger.valueOf(vh.getFirstChildNodeByNodeName("singleton_level").nt().trim());
    
    importScriptIdList       = new ArrayList<String>();
    //加载其它脚本信息序列
    List<IViewHandler> cList = 
        vh.getFirstChildNodeByNodeName("load_script").getChildNodesByNodeName("value");
    for(IViewHandler cVh:cList) {
      importScriptIdList.add(cVh.nt().trim());
    }
    
    //加载需要代理支持脚本
    importRouteScriptIdList = new ArrayList<String>();
    cList = vh.getFirstChildNodeByNodeName("route_script").getChildNodesByNodeName("value");
    for(IViewHandler cVh:cList) {
      importRouteScriptIdList.add(cVh.nt().trim());
    }
    
    //传入参数区
    IViewHandler inField  = vh.getFirstChildNodeByNodeName("in");
    //传入参数是否为序列 已取消传参为序列方式
    //inFieldList           = SBoolean.valueOf(inField.a("list").trim());
    
    inFields              = new ArrayList<ScriptFieldVO>();
    cList                 = inField.getChildNodesByNodeName("field");
    ScriptFieldVO sfVO; //字段信息对象
    for(IViewHandler cVh:cList) {
      sfVO              = new ScriptFieldVO();
      sfVO.name         = cVh.getFirstChildNodeByNodeName("name").nt().trim();
      sfVO.explain      = cVh.getFirstChildNodeByNodeName("explain").nt();
      sfVO.type         = cVh.getFirstChildNodeByNodeName("type").nt().trim();
      sfVO.defValue     = cVh.getFirstChildNodeByNodeName("default_value").nt().trim();
      sfVO.notNull      = SBoolean.valueOf(cVh.getFirstChildNodeByNodeName("not_null").nt().trim());
      sfVO.needB64Dec   = SBoolean.valueOf(cVh.getFirstChildNodeByNodeName("base64_deccode").nt().trim());
      inFields.add(sfVO);
    }
    
    //返回参数信息段
    IViewHandler outField = vh.getFirstChildNodeByNodeName("out");
    outType               = outField.getFirstChildNodeByNodeName("type").nt().trim();
    outExplain            = outField.getFirstChildNodeByNodeName("explain").nt();
    
    //维护信息
    devCode               = vh.getFirstChildNodeByNodeName("oper_dev_code").nt().trim();
    devName               = vh.getFirstChildNodeByNodeName("oper_dev_name").nt().trim();
    uUser                 = vh.getFirstChildNodeByNodeName("oper_dev_uuser").nt().trim();
    uMan                  = vh.getFirstChildNodeByNodeName("oper_dev_uman").nt().trim();
    uCount                = vh.getFirstChildNodeByNodeName("oper_dev_ucount").nt().trim();
    modifyContent         = vh.getFirstChildNodeByNodeName("modify_content").nt().trim();
    
    //代码内容
    sourceContent         = vh.getFirstChildNodeByNodeName("content").nt();
    
    if (sourceFileVer.length() > 0 && !isSyncSubmit) {
      // 导入时带过来的源码MD5值
      String importMd5 = vh.getFirstChildNodeByNodeName("import_content_md5").nt();
      if (importMd5.equalsIgnoreCase(MD5.getMD5Value(sourceContent))) {
        // 如果导入进来的代码没有做过修改，则标记为来源代码版本号
        isSyncSubmit = true;
      }
    }
    errorMsg  = null;
    errorFlag = false;
    hasSourceFile = true;
  }
  
  /**
   * 覆盖方法
   */
  @Override
    public String toString() {
    //构造返回值
    StringBuffer sbf = new StringBuffer();
    sbf
      .append("id:[").append(id).append("]\n")
      .append("className:[").append(className).append("]\n")
      .append("noSingleCompile:[").append(noSingleCompile).append("]\n")
      .append("independentClass:[").append(independentClass).append("]\n")
      
      .append("initMethodName:[").append(initMethodName).append("]\n")
      .append("afterStartMethodName:[").append(afterStartMethodName).append("]\n")
      .append("destoryMethodName:[").append(destoryMethodName).append("]\n")
      .append("interfaceClasspath:[").append(interfaceClasspath).append("]\n")
      .append("extLibPaths:[").append(StringUtil.arr2str(extLibPaths)).append("]\n")
      .append("haveExtLibPaths:[").append(haveExtLibPaths).append("]\n")
      .append("isActionBean:[").append(isActionBean).append("]\n")
      .append("registerID:[").append(registerID).append("]\n")
      //.append("isStatic:[").append(isStatic).append("]\n")
      .append("isParent:[").append(isParent).append("]\n")
      .append("actionSpecialCode:[").append(actionSpecialCode).append("]\n")
      .append("isShadow:[").append(isShadow).append("]\n")
      
      //已取消，请看变量声明处的注释
      //.append("clusterCallMaster:[").append(clusterCallMaster).append("]\n")
      //.append("clusterReceive:[").append(clusterReceive).append("]\n")
      
      .append("clusterMasterTaskRun:[").append(clusterMasterTaskRun).append("]\n")
      .append("clusterCallOnly:[").append(clusterCallOnly).append("]\n")
      .append("enabledActionCharge:[").append(enabledActionCharge).append("]\n")
      
      .append("isDbTransaction:[").append(isDbTransaction).append("]\n")
      .append("isClassVar:[").append(isClassVar).append("]\n")
      .append("noOutLog:[").append(noOutLog).append("]\n")
      .append("isSync:[").append(isSync).append("]\n")
      .append("singletonLevel:[").append(singletonLevel).append("]\n")
      .append("sourceFilePath:[").append(sourceFilePath).append("]\n")
      .append("classFilePath:[").append(classFilePath).append("]\n")
      .append("scriptObj:[").append(scriptObj).append("]\n")
      .append("sourceFileVer:[").append(sourceFileVer).append("]\n")
      .append("classFileVer:[").append(classFileVer).append("]\n")
      .append("sourceFileTime:[").append(sourceFileTime).append("]\n")
      .append("classFileTime:[").append(classFileTime).append("]\n")
      .append("subPath:[").append(subPath).append("]\n")
      .append("classPath:[").append(classPath).append("]\n")
      .append("parentBeanID:[").append(parentBeanID).append("]\n")
      .append("title:[").append(title).append("]\n")
      .append("createTime:[").append(createTime).append("]\n")
      .append("errorFlag:[").append(errorFlag).append("]\n")
      .append("errorMsg:[").append(errorMsg).append("]\n")
    
        .append("taskRunCount:[").append(taskRunCount).append("]\n")
        .append("maxExecuteTime:[").append(maxExecuteTime).append("]\n")
        .append("minExecuteTime:[").append(minExecuteTime).append("]\n")
        .append("lastExecuteTime:[").append(lastExecuteTime).append("]\n")
        .append("runErrorCount:[").append(runErrorCount).append("]\n")
        .append("runErrorTime:[").append(str(runErrorTime)).append("]\n")
        .append("runErrorMsg:[").append(str(runErrorMsg)).append("]\n")
        .append("hasNewRunError:[").append(hasNewRunError).append("]\n")
        
        .append("nativeScript:[").append(nativeScript).append("]\n")
        .append("hasNativeScript:[").append(hasNativeScript).append("]\n")
      .append("nativeClass:[").append(nativeClass).append("]\n")
      .append("nativeClassPath:[").append(nativeClassPath).append("]\n")
      .append("hasNativeClass:[").append(hasNativeClass).append("]\n");
    
    if(importScriptIdList.size()>0) {
      sbf.append("Import Script Id:\n");
      for(String id:importScriptIdList) {
        sbf.append("\t").append(id).append("\n");
      }
    }
    if(importRouteScriptIdList.size()>0) {
      sbf.append("Import RouteScript Id:\n");
      for(String id:importRouteScriptIdList) {
        sbf.append("\t").append(id).append("\n");
      }
    }
    
    //已取消传参为序列方式
    //sbf.append("InFields(isList:"+inFieldList+"):\n");
    
    //传入参数名序列
    for(ScriptFieldVO field:inFields) {
      sbf.append("\t").append(field).append("\n");
    }

    sbf.append("\nOutType:["+outType+"]\n");
    sbf.append("OutExplain:["+outExplain+"]\n\n");
    
    sbf
      .append("\n\nScript Explain:[").append(explain).append("]\n\n")
      .append("\n\ndevCode:[").append(devCode).append("]\n")
      .append("\n\ndevName:[").append(devName).append("]\n")
      .append("\n\nuUser:[").append(uUser).append("]\n")
      .append("\n\nuMan:[").append(uMan).append("]\n")
      .append("\n\nuCount:[").append(uCount).append("]\n\n")
      .append("\n\nmodifyContent:[").append(modifyContent).append("]\n\n")
      .append("\n\nSourceContent:\n").append(sourceContent).append("\n");
    
    return sbf.toString();
  }
  
  /**
   * 源文件修改时间是否发生变化
   * @return true是  false否
   * 2014年7月31日
   * @author 马宝刚
   */
  public boolean isSourceFileChanged() {
    //源文件上一次更新时间
    long sourceLastTime = getSourceLastModified();
        return sourceLastTime != 0 && sourceFileTime != sourceLastTime;
    }
  
  
  /**
   * 编译后的文件时间是否发生变化
   * @return 发生变化
   * 2014年9月1日
   * @author 马宝刚
   */
  public boolean isClassFileChanged() {
    if(sourceFilePath==null) {
      //没有源文件
      return false;
    }
    if(sourceFileStatus!=0) {
      return false;
    }
    try {
      return SFilesUtil.getFileByName(sl.getClassBasePath()+classFilePath,null).lastModified()!=classFileTime;
    }catch(Exception e) {
    }
    return false;
  }
  
  /**
   * 更新编译后的文件时间
   * 2014年9月1日
   * @author 马宝刚
   */
  public void updateClassFileTime() {
    try {
      classFileTime = SFilesUtil.getFileByName(sl.getClassBasePath()+classFilePath,null).lastModified();
    }catch(Exception e) {}
    cf = null;
  }
  
  /**
   * 更新源文件最后修改时间
   * 2014年7月31日
   * @author 马宝刚
   */
  public void updateSourceLastChanged() {
    sourceFileTime = getSourceLastModified();
  }
  
  /**
   * 获取脚本源文件的最后更新时间
   * @return           脚本文件的最后更新时间
   * @throws Exception 异常
   * 2014年7月23日
   * @author 马宝刚
   */
  public long getSourceLastModified() {
    if(sourceFileStatus!=0) {
      return System.currentTimeMillis();
    }
    try {
      sourceFileTime = SFilesUtil.getFileByName(sl.getSourceBasePath()+sourceFilePath,null).lastModified();
    }catch(Exception e) {
      return 0;
    }
    return sourceFileTime;
  }
  
  /**
   * 检测之前编译时的脚本加载器版本号跟当前脚本加载器的版本号是否不同
   * @return 是否当时编译的脚本加载器已经变化
   * 2015年11月25日
   * @author 马宝刚
   */
  public boolean loaderChanged() {
    if(independentClass) {
      //传统类脚本不受脚本类加载器影响，直接返回否
      return false;
    }
      //脚本类中记载的加载器版本号
      if(loaderVer==null || loaderVer.length()<1) {
        return false;
      }
      if(SInteger.valueOf(loaderVer)!=ScriptLoader.SCRIPT_LOAD_VER) {
        if(nativeScript || nativeClass) {
          //构建错误信息
          String errMsg = "\n\n!!!! Native Script Ver Error!!! NativeScriptVer:["+loaderVer+"] CurrnetScriptLoaderVer:["+ScriptLoader.SCRIPT_LOAD_VER+"]!!!\n";
          if(sl==null) {
            System.err.println(errMsg);
          }else {
            sl.error(errMsg);
          }
        }
          return true;
      }
      return false;
  }
  
  /**
   * 清空容器
   * 2016年7月19日
   * @author MBG
   */
  public void clear() {
    id                   = null;
    className            = null;
    //isStatic             = false;
    initMethodName       = null;
    afterStartMethodName = null;
    destoryMethodName    = null;
    registerID           = null;
    isShadow             = false;
    disabled             = false;
    singletonLevel       = 0;
    sourceFilePath       = null;
    buildSourceFilePath  = null;
    classFilePath        = null;
    scriptObj            = null;
    sourceFileVer        = null;
    classFileVer         = null;
    sourceFileTime       = 0;
    classFileTime        = 0;
    subPath              = null;
    classPath            = null;
    title                = null;
    createTime           = null;
    explain              = null;
    sourceContent        = null;
    errorMsg             = null;
    errorFlag            = false;
    sourceFileStatus     = 0;
    actionSpecialCode    = false;
    noOutLog             = false;
    isParent             = false;
    parentBeanID         = null;
    interfaceClasspath   = null;
    extLibPaths          = null;
    haveExtLibPaths      = false;
    isActionBean         = false;
    hasSourceFile        = false;
    isDbTransaction      = false;
    isClassVar           = false;
    
    //已取消，请看变量声明处的注释
    //clusterCallMaster  = false;
    //clusterReceive     = false;
    
    clusterMasterTaskRun = false;
    clusterCallOnly      = false;
    enabledActionCharge  = false;
    nativeScript         = false;
    hasNativeScript      = false;
    nativeClass          = false;
    nativeClassPath      = null;
    hasNativeClass       = false;
    cf                   = null;
    
    devCode              = null;
    devName              = null;
    modifyContent        = null;
    uUser                = null;
    uMan                 = null;
    uCount               = null;
  }
  
  /**
   * 解析内容
   * @param content  文件内容
   * @param subPath  相对路径
   * @param basePath 压缩包文件全路径
   * @return         解析后的当前信息类
   * 2016年7月19日
   * @author MBG
   */
  private ScriptVO parseContent(String content,String subPath,String basePath) {
    clear();
    if(content==null || content.length()<1) {
      return this;
    }
    IViewHandler vh; //脚本对象处理类
    try {
      vh = FNodeHandler.newNodeHandler("UTF-8").setNodeBody(content);
      vh.setXmlStyle(true); //标记为处理xml内容
    }catch(Exception e) {
      e.printStackTrace();
      addErrorMsg("Load Script Source File:["+subPath+"] BasePath:["
      +basePath+"] Exception:"+DebugUtil.getExceptionInfo(e,"\n"));
      return this;
    }
    setValueFromVh(vh); //从xml中获取脚本信息
    this.subPath = SFilesUtil.getFilePath(subPath,false);
    this.id = SFilesUtil.getFileBeforeName(subPath);
    return this;
  }
  
  /**
   * 解析脚本文件
   * @param subPath  脚本相对路径
   * @param basePath 脚本根路径
   * @return         加载信息后的当前脚本信息类
   * 2016年7月19日
   * @author MBG
   */
  public ScriptVO parse(String subPath,String basePath) {
    clear();
    if(basePath!=null && (new File(basePath)).isFile()) {
      //此乃压缩包是也
      return parseContent(SFilesUtil.getZipFileContent(subPath,basePath,"UTF-8"),subPath,basePath);
    }
    //文件夹
    File cFile;
    try {
      cFile = SFilesUtil.getFileByName(subPath,basePath);
    }catch(Exception e) {
      addErrorMsg("Load Script Source File:["+subPath+"] BasePath:["+basePath
          +"] Exception:"+DebugUtil.getExceptionInfo(e,"\n"));
      return this;
    }
    if(!cFile.exists()) {
      addErrorMsg("Not Find The Script Source File:["+subPath+"] BasePath:["+basePath+"]");
      return this;
    }
    IViewHandler vh; //脚本对象处理类
    try {
      vh = FNodeHandler.newNodeHandler("UTF-8").setFile(cFile);
      vh.setXmlStyle(true); //标记为处理xml内容
    }catch(Exception e) {
      e.printStackTrace();
      addErrorMsg("Load Script Source File:["+subPath+"] BasePath:["+basePath
          +"] Exception:"+DebugUtil.getExceptionInfo(e,"\n"));
      return this;
    }
    setValueFromVh(vh); //从xml中获取脚本信息
    this.subPath = SFilesUtil.getFilePath(subPath,false);
    this.id = SFilesUtil.getFileBeforeName(subPath);
    return this;
  }
  
  /**
   * 拼装编译前Java类中需要保存的配置信息
   * @return 编译前Java类中需要保存的配置信息
   * 
   * 注意：修改当前方法中的参数数量后，需要修改ScriptInfo类中设置的数量，和注释
   *       还有当前类中的 setClassInfo 方法
   * 
   * 2016年11月10日
   * @author MBG
   */
  public String buildScriptInfo() {
    //构建返回值
    StringBuffer reSbf = new StringBuffer();
    
    reSbf
      .append("@ScriptInfo({")
      
      .append("\"").append(ScriptLoader.SCRIPT_LOAD_VER).append("\"")
      .append(",\"").append(str(id)).append("\"")
      .append(",\"").append(str(className)).append("\"")
      //.append(",\"").append(str(isStatic)).append("\"")
      .append(",\"").append(str(initMethodName)).append("\"")
      .append(",\"").append(str(afterStartMethodName)).append("\"")
      
      .append(",\"").append(str(destoryMethodName)).append("\"")
      .append(",\"").append(str(registerID)).append("\"")
      .append(",\"").append(str(isShadow)).append("\"")
      .append(",\"").append(str(disabled)).append("\"")
      .append(",\"").append(str(singletonLevel)).append("\"")
      
      .append(",\"").append(str(classFilePath)).append("\"")
      .append(",\"").append(str(classFileVer)).append("\"")
      .append(",\"").append(str(subPath)).append("\"")
      .append(",\"").append(str(classPath)).append("\"")
      .append(",\"").append(BaseUtil.swapString(str(title),"\"","\\\"")).append("\"")
      
      .append(",\"").append(str(actionSpecialCode)).append("\"")
      .append(",\"").append(str(noOutLog)).append("\"")
      .append(",\"").append(str(isParent)).append("\"")
      .append(",\"").append(str(parentBeanID)).append("\"")
      .append(",\"").append(str(interfaceClasspath)).append("\"")
      
      .append(",\"").append(StringUtil.arr2str(extLibPaths,";")).append("\"")
      .append(",\"").append(str(isDbTransaction)).append("\"")
      //已取消，请看变量声明处的注释
      //.append(",\"").append(str(clusterCallMaster)).append("\"")
      //.append(",\"").append(str(clusterReceive)).append("\"")
      .append(",\"").append(str(independentClass)).append("\"")
      .append(",\"").append(str(isSync)).append("\"")
      .append(",\"").append(str(clusterMasterTaskRun)).append("\"")
      
      .append(",\"").append(str(clusterCallOnly)).append("\"")
      .append(",\"").append(str(enabledActionCharge)).append("\"")
      .append(",\"").append(str(createTime)).append("\"")
      .append(",\"").append(str(isClassVar)).append("\"")
      .append("})");
    return reSbf.toString();
  }
  
  /**
   * 设置该脚本的扩展库相对路径
   * @param extLibs 扩展库相对路径
   * 2019年1月16日
   * @author MBG
   */
  public void setExtLibs(String[] extLibs) {
    extLibPaths = extLibs;
        haveExtLibPaths = extLibs != null && extLibs.length > 0;
  }
  
  /**
   * 设置从类中提取出来的配置信息
   * 
   * 注意：传统脚本类是不能加载这个的；
   * 因为传统脚本类中，不能自动增加ScriptInfo信息
   * 
   * @param infos 从类中提取出来的配置信息
   * 2016年11月11日
   * @author MBG
   */
  public void setClassInfo(String[] infos) {
    if(infos==null || infos.length<ScriptInfo.length) {
      return;
    }
    int i = 0; //索引
    
    //0~3
    String value = str(infos[i++]);
    if(value.length()>0) {
      loaderVer = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      id = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      className = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      initMethodName = value;
    }
    
    //4~8
    value = str(infos[i++]);
    if(value.length()>0) {
      afterStartMethodName = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      destoryMethodName = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      registerID = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      isShadow = SBoolean.valueOf(value);
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      disabled = SBoolean.valueOf(value);
    }
    
    //9~13
    value = str(infos[i++]);
    if(value.length()>0) {
      singletonLevel = SInteger.valueOf(value);
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      classFilePath = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      classFileVer = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      subPath = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      classPath = value;
    }
    
    //14~18
    value = str(infos[i++]);
    if(value.length()>0) {
      title = value;
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      actionSpecialCode = SBoolean.valueOf(value);
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      noOutLog = SBoolean.valueOf(value);
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      isParent = SBoolean.valueOf(value);
    }
    value = str(infos[i++]);
    if(value.length()>0) {
      parentBeanID = value;
    }
    
    //19
    value = str(infos[i++]);
    if(value.length()>0) {
      interfaceClasspath = value;
    }
    //20
    value = str(infos[i++]);
    if(value.length()>0) {
      isDbTransaction = SBoolean.valueOf(value);
    }
    //21
    value = str(infos[i++]);
    if(value.length()>0) {
      independentClass = SBoolean.valueOf(value);
    }
    //22
    value = str(infos[i++]);
    if(value.length()>0) {
      isSync = SBoolean.valueOf(value);
    }
    //23
    value = str(infos[i++]);
    if(value.length()>0) {
      clusterMasterTaskRun = SBoolean.valueOf(value);
    }
    //24
    value = str(infos[i++]);
    if(value.length()>0) {
      clusterCallOnly = SBoolean.valueOf(value);
    }
    //25
    value = str(infos[i++]);
    if(value.length()>0) {
      enabledActionCharge = SBoolean.valueOf(value);
    }
    //26
    value = str(infos[i++]);
    if(value.length()>0) {
      createTime = value;
    }
    //27
    value = str(infos[i++]);
    if(value.length()>0) {
      isClassVar = SBoolean.valueOf(value);
    }
  }
  
  /**
   * 强制转换为字符串
   * @param str 待转换值
   * @return    转换后的值
   * 2016年11月10日
   * @author MBG
   */
  private String str(Object str) {
    if(str==null) {
      return "";
    }else if(str instanceof Boolean) {
      if((Boolean)str) {
        return "1";
      }else {
        return "0";
      }
    }
    return str.toString();
  }
  
  /**
   * 通过脚本类获取保存在脚本类注解中的脚本配置信息
   * @param scriptCls 脚本类
   * @return          脚本配置信息
   * 2016年11月27日
   * @author MBG
   */
  public static ScriptVO getScriptInfo(Class<?> cls) {
    //构建返回值
    ScriptVO sVO = new ScriptVO();
    //获取类注解信息
    String[] infos = ClassUtil.getScriptInfo(cls);
    if(infos==null || infos.length<28) {
      return sVO;
    }
    int i = 0; //索引
    sVO.loaderVer            = infos[i++];
    sVO.id                   = infos[i++];
    sVO.className            = infos[i++];
    //sVO.isStatic             = SBoolean.valueOf(infos[i++]);
    sVO.initMethodName       = infos[i++];
    sVO.afterStartMethodName = infos[i++];
    
    sVO.destoryMethodName    = infos[i++];
    sVO.registerID           = infos[i++];
    sVO.isShadow             = SBoolean.valueOf(infos[i++]);
    sVO.disabled             = SBoolean.valueOf(infos[i++]);
    sVO.singletonLevel       = SInteger.valueOf(infos[i++]);
    
    sVO.classFilePath        = infos[i++];
    sVO.classFileVer         = infos[i++];
    sVO.subPath              = infos[i++];
    sVO.classPath            = infos[i++];
    sVO.title                = infos[i++];
    
    sVO.actionSpecialCode    = SBoolean.valueOf(infos[i++]);
    sVO.noOutLog             = SBoolean.valueOf(infos[i++]);
    sVO.isParent             = SBoolean.valueOf(infos[i++]);
    sVO.parentBeanID         = infos[i++];
    sVO.interfaceClasspath   = infos[i++];
    
    sVO.extLibPaths          = BaseUtil.split(infos[i++],";");
    sVO.isDbTransaction      = SBoolean.valueOf(infos[i++]);
    sVO.independentClass     = SBoolean.valueOf(infos[i++]);
    sVO.isSync               = SBoolean.valueOf(infos[i++]);
    sVO.clusterMasterTaskRun = SBoolean.valueOf(infos[i++]);
    
    sVO.clusterCallOnly      = SBoolean.valueOf(infos[i++]);
    sVO.enabledActionCharge  = SBoolean.valueOf(infos[i++]);
    sVO.createTime           = infos[i++];
    sVO.isClassVar           = SBoolean.valueOf(infos[i++]);
    sVO.haveExtLibPaths      = sVO.extLibPaths != null && sVO.extLibPaths.length > 0;
    return sVO;
  }
  
  /**
   * 在保存脚本时，新构建的脚本信息类中需要保存之前脚本信息类中，动态设置的一些值
   * @param sVO 旧的脚本信息类
   * 2019年1月22日
   * @author MBG
   */
  public void setStatusValue(ScriptVO sVO) {
    registerBeanIDs = sVO.registerBeanIDs;
    
    //这里不能直接用老的sVO中的值，因为如果老的sVO中，没有扩展包，但保存后的sVO中存在扩展包，
    //保存都的sVO中直接用了老的sVO的haveExtLibPaths的false值，就会发生错误
        haveExtLibPaths = extLibPaths != null && extLibPaths.length > 0;
    isActionBean    = sVO.isActionBean;
  }
}